#ifndef __UTIL_H__
#define __UTIL_H__

#include "bundle.h"
#include <jsoncpp/json/json.h>
#include <experimental/filesystem>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <memory>
#include <sys/stat.h>
#include <iconv.h> // 编码转换库

namespace Cloud
{
    namespace fs = std::experimental::filesystem;

    // 文件操作相关实用工具类
    class FileUtil
    {
    public:
        FileUtil(const std::string &filepath) :_filepath(filepath) {}

        // 获取文件大小
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filepath.c_str(), &st) < 0)
            {
                std::cerr << "Get fileSize fail!" << std::endl;
                return -1;
            }

            return st.st_size;
        }

        // 获取文件最后一次访问、创建、修改时间
        time_t LastAccTime()
        {
            struct stat st;
            if(stat(_filepath.c_str(), &st))
            {
                std::cerr << "Get access time fail!" << std::endl;
                return -1;
            }

            return st.st_atime;
        }
        time_t LastCreTime()
        {
            struct stat st;
            if(stat(_filepath.c_str(), &st))
            {
                std::cerr << "Get create time fail!" << std::endl;
                return -1;
            }

            return st.st_ctime;
        }
        time_t LastModTime()
        {
            struct stat st;
            if(stat(_filepath.c_str(), &st))
            {
                std::cerr << "Get modify time fail!" << std::endl;
                return -1;
            }

            return st.st_mtime;
        }

        // 获取文件名
        std::string FileName()
        {
            // 获取最后一个 '/'
            size_t pos = _filepath.find_last_of(R"(\/)"); // R"()" 字符不参与转义

            if(pos == std::string::npos)
                return _filepath;

            return _filepath.substr(pos + 1, _filepath.size() - pos);
        }

        // 获取文件 pos 位置起，长度为 len 的数据
        bool GetPosLen(std::string *body, size_t pos, size_t len)
        {
            // 如果读取的内容超出范围，则无法读取
            size_t fsize = this->FileSize();
            if(pos + len > fsize)
            {
                std::cerr << "FileSize less than Pos+Len!" << std::endl;
                return false;
            }

            // 打开文件进行读取
            std::ifstream ifs;
            ifs.open(_filepath, std::ios::binary);
            if(ifs.is_open() == false)
            {
                std::cerr << "Read open file error!" << std::endl;
                return false;
            }

            // 读取指针偏移至指定位置
            ifs.seekg(pos, std::ios::beg);
            body->resize(len); // 提前扩容

            // 读取指定长度的数据
            ifs.read(&((*body)[0]), len);
            if(ifs.good() == false)
            {
                std::cerr << "Read file fail!" << std::endl;
                ifs.close();
                return false;
            }

            ifs.close();
            return true;
        }

        // 获取文件所有数据
        bool GetContent(std::string *body)
        {
            // 复用接口
            size_t fsize = this->FileSize();
            return GetPosLen(body, 0, fsize);
        }

        // 设置文件内容
        bool SetContent(const std::string &body)
        {
            // 打开文件并进行写入
            std::ofstream ofs;
            ofs.open(_filepath, std::ios::binary);
            if(ofs.is_open() == false)
            {
                std::cerr << "Write open file fail!" << std::endl;
                return false;
            }

            // // 编码转换: gbk -> utf8
            // std::string ret = EncodingConver().GBKToUTF8(body);

            // 写入数据
            ofs.write(body.c_str(), body.size());
            if(ofs.good() == false)
            {
                std::cerr << "Write file fail!" << std::endl;
                ofs.close();
                return false;
            }

            ofs.close();
            return true;
        }

        // 删除文件
        bool Remove()
        {
            if(Exists() == false)
            {
                std::cerr << "Remove file fail!" << std::endl;
                return false;
            }

            remove(_filepath.c_str());

            return true;
        }

        // 压缩文件
        bool Compress(const std::string &filename)
        {
            // 1.读取文件
            std::string body;
            if(this->GetContent(&body) == false)
            {
                std::cerr << "Compress get file data fail!" << std::endl;
                return false;
            }

            // 2.压缩文件
            std::string packed = bundle::pack(bundle::LZIP, body);

            // 3.将压缩的数据写入新文件中
            FileUtil fu(filename);
            if(fu.SetContent(packed) == false)
            {
                std::cerr << "Compress set file data fail!" << std::endl;
                return false;
            }

            return true;
        }

        // 解压文件
        bool UnCompress(const std::string &filepath)
        {
            // 1.读取文件
            std::string body;
            if(this->GetContent(&body) == false)
            {
                std::cerr << "UnCompress get file data fail!" << std::endl;
                return false;
            }

            // 2.解压文件
            std::string unpacked = bundle::unpack(body);

            // 3.将解压后的数据写入新文件中
            FileUtil fu(filepath);
            if(fu.SetContent(unpacked) == false)
            {
                std::cerr << "UnCompress set file data fail!" << std::endl;
                return false;
            }

            return true;
        }

        // 判断文件是否存在
        bool Exists()
        {
            return fs::exists(_filepath);
        }

        // 创建目录
        bool CreateDirectory()
        {
            // 如果目录存在，就不判断
            if(this->Exists() == true)
                return true;

            // 创建目录
            return fs::create_directories(_filepath);
        }

        // 浏览当前目录中的所有文件
        bool ScanDirectory(std::vector<std::string> *array)
        {
            // 迭代器遍历
            for(auto &p : fs::directory_iterator(_filepath))
            {
                // 如果是目录，就不进行统计（只记录文件）
                if(fs::is_directory(p) == true)
                    continue;
                
                // 记录信息
                array->push_back(fs::path(p).relative_path().string());
            }
        }


    private:
        std::string _filepath;
    };

    // Json操作相关实用工具类
    class JsonUtil
    {
    public:
        // 序列化
        static bool Serialization(const Json::Value &root, std::string *str)
        {
            // 创建一个序列化对象
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            // 序列化
            std::ostringstream oss;
            if(sw->write(root, &oss) != 0)
            {
                std::cerr << "Serialization fail!" << std::endl;
                return false;
            }

            *str = oss.str();

            return true;
        }

        // 反序列化
        static bool UnSerialization(const std::string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

            std::string err;
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
            if(ret == false)
            {
                std::cerr << "UnSerialization fail!" << std::endl;
                return false;
            }

            return true;
        }
    };

    // 编码转换实用工具类
    class EncodingConver
    {
    #define GBK "gb2312"
    #define UTF8 "utf-8"
    public:
        static std::string GBKToUTF8(const std::string &gbk)
        {
            return converHelper(GBK, UTF8, gbk);
        }

        static std::string UTF8ToGBK(const std::string &utf8)
        {
            return converHelper(UTF8, GBK, utf8);
        }

    private:
        static std::string converHelper(const char *fromcode, const char *tocode, const std::string &code)
        {
            size_t cz = code.size();
            char *pc = const_cast<char*>(&code[0]);

            size_t tz = 1000000;
            char tmp[tz] = { 0 };
            int ret = converHelper(fromcode, tocode, pc, &cz, tmp, &tz);
            if(ret == -1)
            {
                std::cerr << "Conver Fail!" << std::endl;
                return "";
            }

            return tmp; // 自动构造string对象
        }

        static int converHelper
            (
                const char *fromcode, 
                const char *tocode, 
                char *in, size_t *insize, char *out, size_t *outsize
            )
        {
            // 创建编码转换的句柄
            iconv_t cd = iconv_open(tocode, fromcode);
            if(cd == (iconv_t*)-1)
            {
                std::cerr << "Open iconv fail!" << std::endl;
                return -1;
            }

            char **pi = &in;
            char **po = &out;

            // 进行编码转换
            size_t ret = iconv(cd, pi, insize, po, outsize);

            if(ret == (size_t)-1)
            {
                std::cerr << "iconv fail!" << std::endl;
                iconv_close(cd);
                return -1;
            }

            // 关闭句柄
            if(iconv_close(cd))
            {
                std::cerr << "Close iconv fail!" << std::endl;
                return -1;
            }
        }
    };
}

#endif